package x10.utilities;

import java.util.BitSet;

public class BitSetUtils {

    /** Utility class of bit-level utilities
     *
     *  @author Denny Sheirer - some functions
     *  @author Chris Lauderdale - couple functions retrieved from internet
     */

     /**
      * Utility method to convert a byte to its bit-inverse, by flipping
      * each of the bits to its complement
      * @param normal
      * @return complement of normal (abnormal?)
      */
    public static byte inverseByte(byte normalByte){
        byte[]temp = new byte[1];
        temp[0] = normalByte;
        BitSet bits = byteToBitSet(normalByte);

        bits.flip(0,8);

        return bitSetToByte(bits);
    }


    /**
     * Converts a byte[] into a BitSet.
     * @author Chris Lauderdale
     * @param bytes
     *            A <code>byte[]</code> representing the bits in <code>bytes</code>.
     * @return A BitSet object representing the input byte[].
     */
    public static BitSet bytesToBitSet(byte[] bytes)
    {
            final BitSet rv=new BitSet(bytes.length * 8);
            bytesToBitSet(bytes,rv);
            return rv;
    }

     /**
     * Converts a byte into a BitSet.
     * @author Denny Sheirer
     * @param byte
     *            A <code>byte</code> representing the bits in <code>bytes</code>.
     * @return A BitSet object representing the input byte.
     */

    public static BitSet byteToBitSet(byte byteToParse)
    {
        final BitSet rv=new BitSet(8);
        byte[] temp = new byte[1];
        temp[0] = byteToParse;
        bytesToBitSet(temp,rv);
        return rv;
    }

    /**
     * Converts a byte[] into a BitSet.
     * @author Chris Lauderdale
     * @param bytes
     *            A <code>byte[]</code> representing the bits in <code>bytes</code>.
     * @param bits
     *            The {@link BitSet} to fill.
     */
    public static void bytesToBitSet(byte[] bytes,final BitSet bits)
    {
            int i, j, k;
            bits.clear();
            for (i = k = 0; i < bytes.length; i++)
                    for (j = 0x80; j != 0; j >>>= 1, k++)
                            if ((bytes[i] & j) != 0)
                                    bits.set(k);
    }

    /**
     * Converts a <code>BitSet</code> into a <code>byte[]</code>.
     * @author Chris Lauderdale
     * @param bits A {@link BitSet}.
     * @param bytes The <code>byte</code> array to fill.
     */
    public static byte bitSetToByte(BitSet bits) {
        byte[] bytes = bitSetToBytes(bits);
        return bytes[0];
    }

    /**
     * Converts a <code>BitSet</code> into a <code>byte[]</code>.
     * @author Chris Lauderdale
     * @param bits A {@link BitSet}.
     * @param bytes The <code>byte</code> array to fill.
     */
    public static void bitSetToBytes(BitSet bits, byte[] bytes)
    {
            int i;
            java.util.Arrays.fill(bytes,(byte)0);
            for(i=bits.nextSetBit(0);i>=0;i=bits.nextSetBit(i+1))
                    bytes[i>>>3]|=0x80>>>(i&7);
    }

    /**
     * Converts a <code>BitSet</code> into a <code>byte[]</code>.
     * @author Chris Lauderdale
     * @param bits A <code>BitSet</code>.
     * @return A <code>byte[]</code> containing the same bits stored in <code>bits</code>.
     */
    public static byte[] bitSetToBytes(BitSet bits)
    {
        final byte[] bytes = new byte[(bits.size() + 7) >>> 3];
            bitSetToBytes(bits,bytes);

            return bytes;
    }

    /**
     * Converts a BitSet into a Hex String.
     * @author Denny Sheirer
     * @param BitSet
     *            A <code>String</code> representing the bits in <code>BitSet</code>.
     * @return A String representing the hex value of the BitSet.
     */

    public static String bitSetToHexString(BitSet bits){

        StringBuilder sb = new StringBuilder();

        byte[] ba = bitSetToBytes(bits);

        for (int i = 0; i < ba.length; i++) {
            sb.append("0x" + String.format("%1$02X", ba[i]) + " ");
        }
        return sb.toString();
    }

    /**
     * Converts a BitSet with 8 bits into an int.
     * @author Denny Sheirer
     * @param BitSet
     * @return an int representing the signed value of the BitSet.
     */

    public static int bitSet8ToInt(BitSet bits){
        int retInt = 0;
        if(bits.length()>8){
            System.out.println("bitSetToInt: bitset is larger than 8 bits -- can't convert");
        } else {
            byte[] ba = new byte[1];
            bitSetToBytes(bits, ba);
            retInt = (int)ba[0];
        }
        return retInt;
    }
    
    /**
     * Converts a BitSet into a signed int.
     * @author Denny Sheirer
     * @param BitSet
     * @param int - count of bits used
     * @return int representing the signed value of the BitSet.
     */
    public static int bitSetToInt(BitSet bits, int bitCount){
        int retVal = 0;
        for(int x = 0; x < bitCount; x++){
            if(bits.get(x)){
                retVal = retVal + (int)(Math.pow(2, x));
            }
        }
        return retVal;
    }

    /**
     * Extracts a byte from a BitSet
     * @author Denny Sheirer
     * @param BitSet
     * @param int - 8-bit offset into the bitset
     * @return byte value at offset
     */
    public static byte getByte(BitSet bits, int offset){
        byte[] tempBytes = bitSetToBytes(bits);

        return tempBytes[offset];
    }

    /**
     * Extracts a nibble and returns it in the specified position of a byte.
     * @author Denny Sheirer
     * @param BitSet
     * @param String which - UPPER or LOWER nibble
     * @param String returnedAsWhich - return the nibble in the UPPER or LOWER position of the returned byte
     * @return byte with nibble in the returnedAsWhich position
     */
    public static byte getNibble(byte b, String which, String returnedAsWhich){
        BitSet retBS = new BitSet(8);
        BitSet bs = BitSetUtils.byteToBitSet(b);
        int offset;

        if(returnedAsWhich.contentEquals("UPPER")){
            offset = 4;
        } else {
            offset = 0;
        }

        if(which.contentEquals("UPPER")){
            if(bs.get(0)){
                retBS.set(4 - offset);
            }
            if(bs.get(1)){
                retBS.set(5 - offset);
            }
            if(bs.get(2)){
                retBS.set(6 - offset);
            }
            if(bs.get(3)){
                retBS.set(7 - offset);
            }
        } else if (which.contentEquals("LOWER")){
            if(bs.get(4)){
                retBS.set(4 - offset);
            }
            if(bs.get(5)){
                retBS.set(5 - offset);
            }
            if(bs.get(6)){
                retBS.set(6 - offset);
            }
            if(bs.get(7)){
                retBS.set(7 - offset);
            }
        } else {

        }

        return BitSetUtils.bitSetToBytes(retBS)[0];
    }

    /**
     * Reverses (end for end) a BitSet
     * @author Denny Sheirer
     * @param BitSet
     * @param int size - number of bits in the bitset
     * @return BitSet
     */
    public static BitSet reverseBitSet(BitSet bits, int size){
        BitSet returnBits = new BitSet();
        for(int x = 0; x < size; x++){
            if(bits.get(x)){
                returnBits.set(size - 1 - x);
            }
        }
        return returnBits;
    }

    /**
     * Produces string representation (0 and 1) of a BitSet
     * @author Denny Sheirer
     * @param BitSet
     * @return String
     */
    public static String bitSetToString(BitSet bits){
        StringBuilder sb = new StringBuilder();
        for(int x = 0; x < bits.length(); x++){
            if(bits.get(x)){
                sb.append("1 ");
            } else {
                sb.append("0 ");
            }
        }
        return sb.toString();
    }
}

